package k8s

import (
	"context"
	"goproject/api/v1alpha1"
	"goproject/logger"
	"goproject/util"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
)

var chaosMesh *rest.RESTClient

var enoughDuration = util.GetPointer("10m")

// 创建ChaosMesh客户端
func newChaosMeshClient(config *rest.Config) (client *rest.RESTClient) {
	log := logger.GetLogger()
	// 主要参考
	// https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/extend-resources/custom-resource-definition-v1/
	// 部分参考
	// https://www.cnblogs.com/double12gzh/p/13734830.html
	const apiPath = "/apis"
	const group = "chaos-mesh.org"
	const version = "v1alpha1"
	types := []runtime.Object{
		&v1alpha1.BlockChaos{}, &v1alpha1.BlockChaosList{},
		&v1alpha1.DNSChaos{}, &v1alpha1.DNSChaosList{},
		&v1alpha1.JVMChaos{}, &v1alpha1.JVMChaosList{},
		&v1alpha1.HTTPChaos{}, &v1alpha1.HTTPChaosList{},
		&v1alpha1.IOChaos{}, &v1alpha1.IOChaosList{},
		&v1alpha1.KernelChaos{}, &v1alpha1.KernelChaosList{},
		&v1alpha1.NetworkChaos{}, &v1alpha1.NetworkChaosList{},
		&v1alpha1.PodChaos{}, &v1alpha1.PodChaosList{},
		&v1alpha1.StressChaos{}, &v1alpha1.StressChaosList{},
		&v1alpha1.TimeChaos{}, &v1alpha1.TimeChaosList{},

		&v1alpha1.JVMClickHouseChaos{}, &v1alpha1.JVMClickHouseChaosList{},
		&v1alpha1.JVMDruidChaos{}, &v1alpha1.JVMDruidChaosList{},
		&v1alpha1.JVMDubboChaos{}, &v1alpha1.JVMDubboChaosList{},
		&v1alpha1.JVMElasticsearchChaos{}, &v1alpha1.JVMElasticsearchChaosList{},
		&v1alpha1.JVMTarsChaos{}, &v1alpha1.JVMTarsChaosList{}, //
		&v1alpha1.JVMHBaseChaos{}, &v1alpha1.JVMHBaseChaosList{},
		&v1alpha1.JVMHTTPChaos{}, &v1alpha1.JVMHTTPChaosList{},
		&v1alpha1.JVMLogChaos{}, &v1alpha1.JVMLogChaosList{},
		&v1alpha1.JVMMessageChaos{}, &v1alpha1.JVMMessageChaosList{},
		&v1alpha1.JVMMongoDBChaos{}, &v1alpha1.JVMMongoDBChaosList{},
		&v1alpha1.JVMRedisChaos{}, &v1alpha1.JVMRedisChaosList{},
		&v1alpha1.JVMSecurityChaos{}, &v1alpha1.JVMSecurityChaosList{},
		&v1alpha1.JVMServletChaos{}, &v1alpha1.JVMServletChaosList{},
		&v1alpha1.JVMSQLChaos{}, &v1alpha1.JVMSQLChaosList{},
		&v1alpha1.JVMZooKeeperChaos{}, &v1alpha1.JVMZooKeeperChaosList{},

		&v1alpha1.PhysicalMachineChaos{}, &v1alpha1.PhysicalMachineChaosList{},
		// TODO 补全剩余故障类型
	}

	groupVersion := schema.GroupVersion{Group: group, Version: version}

	newSchemeBuilder := runtime.NewSchemeBuilder(func(s *runtime.Scheme) error {
		s.AddKnownTypes(groupVersion, types...)
		metav1.AddToGroupVersion(s, groupVersion)
		return nil
	})
	err := newSchemeBuilder.AddToScheme(scheme.Scheme)
	if err != nil {
		panic(err)
	}

	crdConfig := *config
	crdConfig.ContentConfig.GroupVersion = &groupVersion
	crdConfig.APIPath = apiPath
	crdConfig.NegotiatedSerializer = serializer.NewCodecFactory(scheme.Scheme)
	crdConfig.UserAgent = rest.DefaultKubernetesUserAgent()

	client, err = rest.UnversionedRESTClientFor(&crdConfig)
	if err != nil {
		log.Panicf("创建ChaosMesh客户端失败 error: %s", err.Error())
	}
	return
}

// RecoverFromChaos 从故障中恢复: 通过设置注解 pause=true 暂停故障
func RecoverFromChaos(chaos v1alpha1.InnerObject) {
	log := logger.GetLogger()
	//chaos := getChaosByResource(resource)
	resource := chaos.GetObjectKind().GroupVersionKind().Kind
	name := chaos.GetName()
	err := chaosMesh.Get().Namespace(util.ChaosMeshObjectNamespace).Resource(resource).
		Name(name).Do(context.TODO()).Into(chaos)
	if err != nil {
		log.Panicf("获取故障CRD失败 故障名 %s error: %s", chaos.GetName(), err.Error())
	}
	annotations := chaos.GetAnnotations()
	if annotations == nil {
		annotations = make(map[string]string)
	}
	annotations[v1alpha1.PauseAnnotationKey] = "true"
	chaos.SetAnnotations(annotations)
retry:
	err = chaosMesh.Put().Namespace(util.ChaosMeshObjectNamespace).Resource(resource).
		Name(name).Body(chaos).Do(context.TODO()).Error()

	// 参考 label patch 实现将返回错误 the server rejected our request due to an error in our request

	// 注解而非删除，可以保留实验过程记录
	//err := chaosMesh.Delete().Namespace(ChaosMeshObjectNamespace).Resource(podChaosName).
	//	Name(name).Do(context.TODO()).Error()
	if err != nil {
		// 可能因网络问题失败
		log.Errorf("从故障中恢复失败 将重试 故障名 %s error: %s", name, err.Error())
		goto retry
	}
}

// 故障状态
func GetChaosStatus(chaos v1alpha1.InnerObject) (status ChaosStatus) {
	log := logger.GetLogger()
	resource := chaos.GetObjectKind().GroupVersionKind().Kind
	name := chaos.GetName()
	err := chaosMesh.Get().Namespace(util.ChaosMeshObjectNamespace).Resource(resource).
		Name(name).Do(context.TODO()).Into(chaos)
	if err != nil {
		log.Panicf("获取容器故障状态错误 %s %s error: %s", util.ChaosMeshObjectNamespace, name, err.Error())
	}
	counter := 0
	// 组装故障注入状态
	for _, condition := range chaos.GetStatus().Conditions {
		counter++
		switch condition.Type {
		case "Selected":
			status.Selected = condition.Status == "True"
		case "AllInjected":
			status.AllInjected = condition.Status == "True"
		case "AllRecovered":
			status.AllRecovered = condition.Status == "True"
		case "Paused":
			status.Paused = condition.Status == "True"
		default:
			counter--
			log.Warnf("获取容器故障状态错误 未知状态字段 Type: %s, Status %s",
				condition.Type, condition.Status)
		}
	}
	if counter != 4 {
		// 刚创建时，参数个数总是0，符合预期
	}
	return
}
